# Elgamal_Ecc.py
# 椭圆曲线上的加密算法
# -*- coding: utf-8 -*-
"""
Created on 2021

@author: Ximing
"""

import collections
from ECC import *
import random
Coord = collections.namedtuple("Coord", ["x", "y"])

class Elgamal_Ecc(object):
    def __init__(self, ec):
        """elliptic curve as: (y**2 = x**3 + a * x + b) mod q
        """
        self.Ecc = ec
        self.G = Coord(0, 0)

        self.Pub = Coord(0, 0)
        self.init_Para()
        self.Ecc_points = self.Ecc.Ecc_points
        pass

    def init_Para(self):
        """generate G
        """
        while True:
            r = randrange(self.Ecc.q)
            g, gg = self.Ecc.at(r)
            if g.x == gg.x and g.y == gg.y:
                continue
            if self.Ecc.order(g) > 10:
                break
        pass
        self.G = g

    def gen(self, x):
        """produce public key, which is point xG
        """

        return self.Ecc.mul(self.G, x)

    def get_indexofPoint(self, point):
        for index in range(len(self.Ecc_points)):
            if (self.Ecc_points[index][0] == point.x):
                if (self.Ecc_points[index][1] == point.y):
                    return index
        return 0

    def get_PointofPoints(self, index):
        m_x = self.Ecc_points[index][0]
        m_y = self.Ecc_points[index][1]
        return Coord(m_x, m_y)


    def enc(self, plain_text, pub):
        c = []
        for char in plain_text:
            r = random.randint(2, 10)   #r is a random number
            c1 = self.Ecc.mul(self.G, r)  # kG
            rQx = self.Ecc.mul(pub, r)
            intchar = ord(char)
            point = self.get_PointofPoints(intchar)  # 映射为椭圆曲线上的点
            c2 =self.Ecc.add(rQx, point)
            c.append([c1, c2])
        return c

    def dec(self, cipher, priv):
        dectext = []
        for ch in cipher:
            c1, c2 = ch
            point = self.Ecc.mul(c1, priv)
            p = Coord(point.x, -point.y)
            m2 = self.Ecc.add(c2, p)
            dectext.append(chr(self.get_indexofPoint(m2)))
        return dectext

if __name__ == "__main__":
    print("*************ECC encrpyt and decrypt*************")
    a = 1
    b = 3
    q = 223  # choose from 17,23,43,53,221,223
    ec = EC(a, b, q)
    Elgamal_Ecc = Elgamal_Ecc(ec)

    # show all points on the ECC
    #Elgamal_Ecc.Ecc.show_allpoints()

    # user  key
    priv = 32
    # gen public key
    pub = Elgamal_Ecc.gen(priv)
    print("the private key is {}：".format(priv))
    print("the primitive point of ECC is ({},{})：".format(Elgamal_Ecc.G.x, Elgamal_Ecc.G.y))
    print("Order of the point is {}：".format(Elgamal_Ecc.Ecc.order(Elgamal_Ecc.G)))

    print("the publi key is point ({}, {})：".format(pub.x, pub.y))
    #
    plain_text = "My number is 2312"
    print("plain text is:" + plain_text)
    cipher = Elgamal_Ecc.enc(plain_text, pub)

    print("the cipher is："+"")
    print(cipher)

    dectext = Elgamal_Ecc.dec(cipher, priv)
    print("The decrypted text is：")
    print(dectext)